package org.example.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.micrometer.common.util.StringUtils;
import org.example.entity.SysTestConfig;
import org.example.mapper.SysTestConfigMapper;
import org.example.pojo.DemoDto;
import org.example.pojo.SysTestConfigDto;
import org.example.pojo.SysTestConfigVo;
import org.example.service.SysTestConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @author wxm
 * @date 2025/3/3 9:02
 */
@Service
public class SysTestConfigServiceImpl extends ServiceImpl<SysTestConfigMapper, SysTestConfig> implements SysTestConfigService {
    @Autowired
    private SysTestConfigMapper sysTestConfigMapper;
    @Autowired
    private StringRedisTemplate redisTemplate;

    @Value("${redisData.timeout}")
    private Integer redisDataTimeout;

    @Override
    public List<SysTestConfig> selectConfigs() {
        return sysTestConfigMapper.selectConfig();
    }

    @Override
    public void saveConfig(SysTestConfigDto sysTestConfig) {
        if (sysTestConfig == null || StringUtils.isEmpty(sysTestConfig.getPath())) {
            throw new IllegalArgumentException("Invalid sysTestConfig or path");
        }
        String path = sysTestConfig.getPath();
        // String lockKey = "lock:" + path;
        // try {
        // 尝试获取分布式锁
        // boolean isLocked = redisTemplate.opsForValue().setIfAbsent(lockKey, "locked", 10, TimeUnit.SECONDS);
        // if (!isLocked) {
        //     throw new RuntimeException("Failed to acquire lock for path: " + path);
        // }
        SysTestConfig config = new SysTestConfig();
        config.setUrl(path);
        config.setTableName(sysTestConfig.getTableName());
        config.setContent(sysTestConfig.getContent().toString());
        String redisData = redisTemplate.opsForValue().get(path);
        int count = sysTestConfigMapper.countByUrl(path);
        if (StringUtils.isEmpty(redisData)) {
            try {
                if (count == 0) {
                    sysTestConfigMapper.saveConfig(config);
                    redisTemplate.opsForValue().set(path, JSONObject.toJSONString(config));
                } else {
                    sysTestConfigMapper.upConfig(config);
                    redisTemplate.opsForValue().set(path, JSONObject.toJSONString(config));
                }

            } catch (Exception e) {
                // 记录日志并回滚
                log.error("Failed to save config to database: {}", e);
                redisTemplate.delete(path);
                throw e;
            }
        } else {
            // 如果已有数据，先删除再新增
            redisTemplate.delete(path);
            try {
                if (count == 0) {
                    sysTestConfigMapper.saveConfig(config);
                    redisTemplate.opsForValue().set(path, JSONObject.toJSONString(config));
                } else {
                    sysTestConfigMapper.upConfig(config);
                    redisTemplate.opsForValue().set(path, JSONObject.toJSONString(config));
                }
            } catch (Exception e) {
                // 记录日志并回滚
                log.error("Failed to update config in database: {}", e);
                redisTemplate.delete(path);
                throw e;
            }
        }
        // }finally {
        //     // 释放分布式锁
        //     redisTemplate.delete(lockKey);
        // }
    }

    @Override
    @Transactional
    public String delConfig(Integer id) {
        int i = sysTestConfigMapper.deleteConfig(id);
        return String.format("删除 %s 条数据。", i);
    }

    @Override
    public void upConfig(SysTestConfigDto sysTestConfig) {
        SysTestConfig config = new SysTestConfig();
        config.setUrl(sysTestConfig.getPath());
        config.setTableName(sysTestConfig.getTableName());
        config.setContent(sysTestConfig.getContent().toString());
        sysTestConfigMapper.upConfig(config);
    }

    @Override
    public SysTestConfigVo selectConfig(String url) {
        SysTestConfigVo sysTestConfigVo;
        try {
            String redisData = redisTemplate.opsForValue().get(url);
            if (StringUtils.isEmpty(redisData)) {
                synchronized (this) {
                    redisData = redisTemplate.opsForValue().get(url);
                    if (StringUtils.isEmpty(redisData)) {
                        sysTestConfigVo = sysTestConfigMapper.selectConfigContent(url);
                        redisTemplate.opsForValue().set(url, JSONObject.toJSONString(sysTestConfigVo), redisDataTimeout, TimeUnit.SECONDS);
                    } else {
                        JSONObject jsonObject = JSONObject.parseObject(redisData);
                        sysTestConfigVo = jsonObject.toJavaObject(SysTestConfigVo.class);
                    }
                }
            } else {
                JSONObject jsonObject = JSONObject.parseObject(redisData);
                sysTestConfigVo = jsonObject.toJavaObject(SysTestConfigVo.class);
            }
        } catch (Exception e) {
            log.error("Error occurred while fetching or parsing data from Redis", e);
            return null;
        }
        return sysTestConfigVo;
    }

    @Override
    public List<Map<String, Object>> selectDataByConfig(DemoDto demoDto, String url) {
        List<Map<String, Object>> dataList = Collections.emptyList();
        SysTestConfigVo sysTestConfigVo;
        try {
            String redisData = redisTemplate.opsForValue().get(url);
            if (StringUtils.isEmpty(redisData)) {
                synchronized (this) {
                    redisData = redisTemplate.opsForValue().get(url);
                    if (StringUtils.isEmpty(redisData)) {
                        sysTestConfigVo = sysTestConfigMapper.selectConfigContent(url);
                        redisTemplate.opsForValue().set(url, JSONObject.toJSONString(sysTestConfigVo), redisDataTimeout, TimeUnit.SECONDS);
                    } else {
                        JSONObject jsonObject = JSONObject.parseObject(redisData);
                        sysTestConfigVo = jsonObject.toJavaObject(SysTestConfigVo.class);
                    }
                }
            } else {
                JSONObject jsonObject = JSONObject.parseObject(redisData);
                sysTestConfigVo = jsonObject.toJavaObject(SysTestConfigVo.class);
            }
        } catch (Exception e) {
            log.error("Error occurred while fetching or parsing data from Redis", e);
            return null;
        }
        JSONObject jsonObject = JSONObject.parseObject(sysTestConfigVo.getContent());
        String[] split = sysTestConfigVo.getTableName().split(";");
        if (split.length == 1) {
            List<String> columnNames = getColumnName(jsonObject);
            dataList = sysTestConfigMapper.selectDataByConfig(Arrays.asList(split).get(0),columnNames,demoDto);

        }
        return dataList;
    }

    private List<String> getColumnName(JSONObject jsonObject) {
        List<String> columnNames = new ArrayList<>();
        // 获取 tables 数组
        JSONArray tables = jsonObject.getJSONArray("tables");
        // 遍历 tables
        for (int i = 0; i < tables.size(); i++) {
            JSONObject table = tables.getJSONObject(i);
            JSONArray columns = table.getJSONArray("columns");
            // 遍历 columns
            for (int j = 0; j < columns.size(); j++) {
                JSONObject column = columns.getJSONObject(j);
                // 检查 isSearch 是否为 true
                if (column.getBoolean("isSearch")) {
                    // 获取 columnName
                    String columnName = column.getString("columnName");
                    System.out.println("Column with isSearch=true: " + columnName);
                    columnNames.add(columnName);
                }
            }
        }
        return columnNames;
    }
}
